package com.loanafter.fox.monitor.logback.event;

import ch.qos.logback.classic.spi.ILoggingEvent;
import com.loanafter.fox.monitor.logback.filter.MessagePrefixFilter;

import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author jasonli@kuainiugroup.com
 * @date 2020/7/30 下午5:44
 */
public class EventBuilder {

    private final Event event;

    private boolean alreadyBuilt = false;

    public EventBuilder() {
        this(UUID.randomUUID());
    }

    public EventBuilder(UUID eventId) {
        this.event = new Event(eventId);
    }

    /**
     * Finalises the {@link Event} and returns it.
     * <p>
     * This operations will automatically set the missing values and make the mutable values immutable.
     *
     * @return an immutable event.
     */
    public synchronized Event build() {
        if (alreadyBuilt) {
            throw new IllegalStateException("A message can't be built twice");
        }

        autoSetMissingValues(event);

        // Lock it only when everything has been set, in case of exception it should be possible to try to build again.
        alreadyBuilt = true;
        return event;
    }

    /**
     * Sets default values for each field that hasn't been provided manually.
     *
     * @param event currently handled event.
     */
    private static void autoSetMissingValues(Event event) {
        // Ensure that a timestamp is set (to now at least!)
        if (event.getTimestamp() == null) {
            event.setTimestamp(new Date());
        }
    }

    /**
     * Sets the timestamp in the event.
     *
     * @param timestamp timestamp of the event.
     * @return the current {@code EventBuilder} for chained calls.
     */
    public EventBuilder withTimestamp(Date timestamp) {
        event.setTimestamp(timestamp);
        return this;
    }
    /**
     * Sets the message in the event.
     *
     * @param message message of the event.
     * @return the current {@code EventBuilder} for chained calls.
     */
    public EventBuilder withMessage(String message) {
        event.setMessage(message);
        return this;
    }

    public EventBuilder withMessagePrefix(List<MessagePrefixFilter> filterList) {
        if (filterList != null && !filterList.isEmpty()) {
            StringBuilder builder = new StringBuilder();
            for (MessagePrefixFilter filter : filterList) {
                builder.append(filter.getPrefix()).append(",");
            }
            event.setMessagePrefix(builder.toString());
        }
        return this;
    }
    /**
     * Sets the log level in the event.
     *
     * @param level log level of the event.
     * @return the current {@code EventBuilder} for chained calls.
     */
    public EventBuilder withLevel(Event.Level level) {
        event.setLevel(level);
        return this;
    }
    /**
     * Sets the logger in the event.
     *
     * @param logger logger of the event.
     * @return the current {@code EventBuilder} for chained calls.
     */
    public EventBuilder withLogger(String logger) {
        event.setLogger(logger);
        return this;
    }
    /**
     * Adds a tag to an event.
     * <p>
     * This allows to set a tag value in different contexts.
     *
     * @param tagKey   name of the tag.
     * @param tagValue value of the tag.
     * @return the current {@code EventBuilder} for chained calls.
     */
    public EventBuilder withTag(String tagKey, String tagValue) {
        event.getTags().put(tagKey, tagValue);
        return this;
    }
}
